using System;
using UnityEngine;
using Random = UnityEngine.Random;

namespace CGF
{
    public static class MathHelper
    {
        /// <summary>
        /// 获取贝塞尔曲线上的点
        /// </summary>
        public static Vector3 GetBezierPoint(Vector3 p1, Vector3 p2, Vector3 p3, float t)
        {
            //t范围0到1,对应的贝塞尔起点到终点;
            float x = CalculateQuadSpline(p1.x, p2.x, p3.x, t);
            float y = CalculateQuadSpline(p1.y, p2.y, p3.y, t);
            float z = CalculateQuadSpline(p1.z, p2.z, p3.z, t);
            return new Vector3(x, y, z);
        }

        /// <summary>
        /// 获取贝塞尔曲线上的点
        /// </summary>
        public static Vector2 GetBezierPoint(Vector2 p1, Vector2 p2, Vector2 p3, float t)
        {
            //t范围0到1,对应的贝塞尔起点到终点;
            float x = CalculateQuadSpline(p1.x, p2.x, p3.x, t);
            float y = CalculateQuadSpline(p1.y, p2.y, p3.y, t);
            return new Vector2(x, y);
        }

        /// <summary>
        /// 获取贝塞尔曲线上所有的点
        /// </summary>
        public static Vector3[] GetBezierPoints(Vector3 p1, Vector3 p2, Vector3 p3, int pointsAmount)
        {
            int amount = pointsAmount - 1;
            Vector3[] newVector2 = new Vector3[pointsAmount];
            float tStep = 1 / ((float)amount);
            float t = 0f;
            for (int ik = 0; ik <= amount; ik++)
            {
                float x = CalculateQuadSpline(p1.x, p2.x, p3.x, t);
                float y = CalculateQuadSpline(p1.y, p2.y, p3.y, t);
                float z = CalculateQuadSpline(p1.z, p2.z, p3.z, t);
                newVector2[ik] = new Vector3(x, y, z);
                t = t + tStep;
            }
            newVector2[amount] = p3;
            return newVector2;
        }

        /// <summary>
        /// 获取贝塞尔曲线上所有的点
        /// </summary>
        public static Vector2[] GetBezierPoints(Vector2 p1, Vector2 p2, Vector2 p3, int pointsAmount)
        {
            int amount = pointsAmount - 1;
            Vector2[] newVector2 = new Vector2[pointsAmount];
            float tStep = 1 / ((float)amount);
            float t = 0f;
            for (int ik = 0; ik <= amount; ik++)
            {
                float x = CalculateQuadSpline(p1.x, p2.x, p3.x, t);
                float y = CalculateQuadSpline(p1.y, p2.y, p3.y, t);
                newVector2[ik] = new Vector2(x, y);
                t = t + tStep;
            }
            newVector2[amount] = p3;
            return newVector2;
        }
        static float CalculateQuadSpline(float z0, float z1, float z2, float t)
        {
            float a1 = (float)((1.0 - t) * (1.0 - t) * z0);
            float a2 = (float)(2.0 * t * (1 - t) * z1);
            float a3 = (float)(t * t * z2);
            float a4 = a1 + a2 + a3;
            return a4;
        }

        /// <summary>
        /// 绕任意轴任意角度旋转向量 
        /// </summary>
        public static Vector3 RotateRound(Vector3 position, Vector3 center, Vector3 axis, float angle)
        {
            Vector3 point = Quaternion.AngleAxis(angle, axis) * (position - center);
            Vector3 resultVec3 = center + point;
            return resultVec3;
        }

        /// <summary>
        /// 获取二维平面两条直线的交点
        /// </summary>
        public static Vector2 GetCrossPointOf2Line(Line line1, Line line2)
        {
            return GetCrossPointOf2Line(line1.p1, line1.p2, line2.p1, line2.p2);
        }

        /// <summary>
        /// 获取两直线交点
        /// </summary>
        public static Vector2 GetCrossPointOf2Line(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2)
        {
            float a = 0, b = 0;
            int state = 0;
            if (a1.x != a2.x)
            {
                a = (a2.y - a1.y) / (a2.x - a1.x);
                state |= 1;
            }
            if (b1.x != b2.x)
            {
                b = (b2.y - b1.y) / (b2.x - b1.x);
                state |= 2;
            }
            switch (state)
            {
                case 0: //L1与L2都平行Y轴
                {
                    if (a1.x == b1.x)
                    {
                        return new Vector2(0, 0);
                    }
                    else
                    {
                        return new Vector2(0, 0);
                    }
                }
                case 1: //L1存在斜率, L2平行Y轴
                {
                    float x = b1.x;
                    float y = (a1.x - x) * (-a) + a1.y;
                    return new Vector2(x, y);
                }
                case 2: //L1平行Y轴，L2存在斜率
                {
                    float x = a1.x;
                    float y = (b1.x - x) * (-b) + b1.y;
                    return new Vector2(x, y);
                }
                case 3: //L1，L2都存在斜率
                {
                    if (a == b)
                    {
                        return new Vector2(0, 0);
                    }
                    float x = (a * a1.x - b * b1.x - a1.y + b1.y) / (a - b);
                    float y = a * x - a * a1.x + a1.y;
                    return new Vector2(x, y);
                }
            }
            return new Vector2(0, 0);
        }

        /// <summary>
        /// 获取三维空间两条直线的交点
        /// </summary>
        public static Vector3 GetCrossPoint(Vector3 p1, Vector3 v1, Vector3 p2, Vector3 v2)
        {
            if (Vector3.Dot(v1, v2) == 1)
            {
                Debug.LogError("两线平行");
                return Vector3.zero;
            }
            Vector3 startPointSeg = p2 - p1;
            Vector3 vecS1 = Vector3.Cross(v1, v2);
            Vector3 vecS2 = Vector3.Cross(startPointSeg, v2);
            float num = Vector3.Dot(startPointSeg, vecS1);
            if (num >= 1E-05f || num <= -1E-05f)
            {
                Debug.LogError("四点不共面");
                return Vector3.zero;
            }
            float num2 = Vector3.Dot(vecS2, vecS1) / vecS1.sqrMagnitude;
            return p1 + v1 * num2;
        }

        /// <summary>
        /// 获取两点连线的中点
        /// </summary>
        public static Vector2 GetCenterBetween2Points(Vector2 point1, Vector2 point2)
        {
            return new Vector2((point1.x + point2.x) * 0.5f, (point1.y + point2.y) * 0.5f);
        }

        /// <summary>
        /// 按小数点后几位向下取,不足用0补齐
        /// </summary>
        public static string Floor(double value, int decimals = 2)
        {
            if (decimals <= 0)
            {
                string resultStr = value.ToString();
                int dotIndex = resultStr.IndexOf(".");
                if (dotIndex > 0)
                {
                    return resultStr.Substring(0, dotIndex);
                }
                return resultStr;
            }
            else
            {
                string resultStr = value.ToString("F" + (decimals + 1));
                return resultStr.Substring(0, resultStr.Length - 1);
            }
        }

        //获取两个角的夹角(锐角)
        public static float GetIncAngle(float angle1, float angle2)
        {
            float angle = Mathf.Abs(Angle360(angle1) - Angle360(angle2));
            if (angle > 180) angle = 360 - angle;
            return angle;
        }

        //将角度转为0-360度的值
        public static float Angle360(float angle)
        {
            while (angle < 0)
            {
                angle += 360;
            }
            while (angle > 360)
            {
                angle -= 360;
            }
            return angle;
        }

        //点到线段(非直线)的最小距离(此处未开方，开方即可得到真正的距离，不开方与外部值的平方做比较能大幅提高运算效率)
        public static float GetDistance(Vector2 p, Vector2 a, Vector2 b)
        {
            //平移到p为原点
            a.x -= p.x;
            a.y -= p.y;
            b.x -= p.x;
            b.y -= p.y;
            //线段ab到远点的距离
            float r = (a.x - b.x) * (a.x) + (a.y - b.y) * a.y;
            float d = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
            if (r <= 0) return a.x * a.x + a.y * a.y;
            else if (r >= d) return b.x * b.x + b.y * b.y;
            r /= d;
            float x = a.x + (b.x - a.x) * r, y = a.y + (b.y - a.y) * r;
            return x * x + y * y;
        }

        //判断一点是否在另外两点内测
        public static bool IsInside(Vector2 p, Vector2 a, Vector2 b)
        {
            Vector2 ab = b - a, ap = p - a;
            Vector2 ba = a - b, bp = p - b;
            return Vector2.Dot(ab, ap) > 0 && Vector2.Dot(ba, bp) > 0;
        }

        //求平面内点到直线的垂足
        public static Vector2 GetFootPoint(Vector2 p, Vector2 a, Vector2 b)
        {
            Vector2 d = a - b;
            float u = (p.x - a.x) * (a.x - b.x) + (p.y - a.y) * (a.y - b.y);
            u = u / ((d.x * d.x) + (d.y * d.y));
            return new Vector2(a.x + u * d.x, a.y + u * d.y);
        }

        //判断一点是否在一个不规则多边形内部
        public static bool IsInPolygon(Vector2 point, Vector2[] polygonPoints)
        {
            bool inside = false;
            int pointCount = polygonPoints.Length;
            Vector2 p1, p2;
            for (int i = 0, j = pointCount - 1; i < pointCount; j = i, i++)
            {
                p1 = polygonPoints[i];
                p2 = polygonPoints[j];
                if (point.y < p2.y)
                {
                    if (p1.y <= point.y)
                    {
                        if ((point.y - p1.y) * (p2.x - p1.x) > (point.x - p1.x) * (p2.y - p1.y))
                        {
                            inside = (!inside);
                        }
                    }
                }
                else if (point.y < p1.y)
                {
                    if ((point.y - p1.y) * (p2.x - p1.x) < (point.x - p1.x) * (p2.y - p1.y))
                    {
                        inside = (!inside);
                    }
                }
            }
            return inside;
        }

        /// <summary>
        /// 根据每个索引的概率权重返回随机索引，
        /// 如传入[10,20,30]则有10/60的机率返回0，20/60的机率返回1，30/60的机率返回2，
        /// 返回-1表示运算失败
        /// </summary>
        public static int GetRandomIndex(int[] rates)
        {
            int total = 0;
            for (int i = 0; i < rates.Length; i++)
            {
                total += rates[i];
            }

            int ran = Random.Range(0, total);
            int min = 0;
            for (int i = 0; i < rates.Length; i++)
            {
                int max = min + rates[i];
                if (ran >= min && ran < max)
                {
                    return i;
                }
                min += rates[i];
            }
            return -1;
        }

        /// <summary>
        /// 将小数点太长的浮点四舍五入到指定位数的小数
        /// </summary>
        public static float Round(float value, int decimals = 2)
        {
            return Mathf.Round(value * 100f) / 100f;
        }

        /// <summary>
        /// 最多保留几位小数点
        /// </summary>
        public static string FormatDecimalFloor(float value, int decimalPlaces = 2, bool forceZero = false)
        {
            return FormatDecimal(value, decimalPlaces, -1, forceZero);
        }
        public static string FormatDecimalRound(float value, int decimalPlaces = 2, bool forceZero = false)
        {
            return FormatDecimal(value, decimalPlaces, 0, forceZero);
        }
        public static string FormatDecimalCeil(float value, int decimalPlaces = 2, bool forceZero = false)
        {
            return FormatDecimal(value, decimalPlaces, 1, forceZero);
        }
        private static string FormatDecimal(float value, int decimalPlaces, int roundingType, bool forceZero = false)
        {
            double roundedValue = roundingType switch
            {
                -1 => Math.Floor(value * Math.Pow(10, decimalPlaces)) / Math.Pow(10, decimalPlaces),
                0 => Math.Round(value, decimalPlaces, MidpointRounding.AwayFromZero),
                1 => Math.Ceiling(value * Math.Pow(10, decimalPlaces)) / Math.Pow(10, decimalPlaces),
            };
            if (forceZero)
            {
                // 强制补零
                return roundedValue.ToString($"F{decimalPlaces}");
            }
            else
            {
                // 不补零，使用常规格式化
                string formatString = decimalPlaces == 0 ? "0" : $"0.{new string('#', decimalPlaces)}";
                return roundedValue.ToString(formatString);
            }
        }
    }
}
